import React, {
	cloneElement,
	forwardRef,
	ForwardRefRenderFunction,
	isValidElement,
	ReactElement,
	ReactNode,
	useCallback,
} from 'react';
import useControlledState from '../hooks/use-controlled-state';
import useProps from '../hooks/use-props';
import { ForwardRefProps } from '../utils/interface';
import { noop } from '../utils/misc';
import { PopupSelectProps } from './interface';
import Popup from './Popup';

type PopupSelectRequiredProps = Required<
	Pick<
		PopupSelectProps,
		| 'selectConfirmTrigger'
		| 'selectCancelTrigger'
		| 'selectDefaultValuePropName'
		| 'trigger'
		| 'disabled'
		| 'onClose'
	>
>;

const DEFAULT_PROPS: PopupSelectRequiredProps = {
	selectConfirmTrigger: 'onConfirm',
	selectCancelTrigger: 'onCancel',
	selectDefaultValuePropName: 'defaultValue',
	trigger: 'onClick',
	disabled: false,
	onClose: noop,
};

const InternalPopupSelect: ForwardRefRenderFunction<
	HTMLDivElement,
	PopupSelectProps
> = (_props, ref) => {
	const [
		{
			selectConfirmTrigger,
			selectCancelTrigger,
			selectDefaultValuePropName,
			trigger,
			disabled,
			onClose,
		},
		{
			visible: _visible,
			value: _value,
			defaultValue,
			onChange: _onChange,
			select,
			position,
			children,
			...restProps
		},
	] = useProps<PopupSelectRequiredProps, PopupSelectProps>(
		DEFAULT_PROPS,
		_props,
	);

	const { value: visible, onChange: setVisible } = useControlledState({
		value: _visible,
		defaultValue: false,
	});

	const { value, onChange } = useControlledState({
		value: _value,
		defaultValue,
		onChange: _onChange,
	});

	const call = useCallback(
		(eventName: string, props: any, ...args: any[]) => {
			if (props && typeof props[eventName] === 'function') {
				props[eventName](...args);
			}
		},
		[],
	);

	const renderSelect = (): ReactNode => {
		if (!isValidElement(select)) {
			return select;
		}

		return cloneElement(select, {
			...select.props,
			[selectDefaultValuePropName]: value,
			[selectConfirmTrigger]: (...args: any[]) => {
				call(selectConfirmTrigger, select.props, ...args);
				onChange(args[0]);
				setVisible(false);
			},
			[selectCancelTrigger]: (...args: any[]) => {
				call(selectCancelTrigger, select.props, ...args);
				setVisible(false);
				onClose();
			},
		});
	};

	const renderChildren = (): ReactNode => {
		let child = children;

		if (typeof children === 'function') {
			child = (children as any)(value);
		}

		if (!isValidElement(child)) {
			return child;
		}

		return cloneElement(child, {
			...child.props,
			[trigger]: (...args: any[]) => {
				if (!disabled) {
					setVisible(true);
				}

				call(trigger, child && (child as any).props, ...args);
			},
		});
	};

	return (
		<>
			<Popup
				visible={visible}
				{...restProps}
				position={position || 'bottom'}
				onClose={() => {
					setVisible(false);
					onClose();
				}}
				ref={ref}
			>
				{renderSelect()}
			</Popup>
			{renderChildren()}
		</>
	);
};

InternalPopupSelect.displayName = 'PopupSelect';

const PopupSelect = forwardRef<HTMLDivElement, PopupSelectProps>(
	InternalPopupSelect,
) as <T = any>(
	props: ForwardRefProps<PopupSelectProps<T>, HTMLDivElement>,
) => ReactElement;

export default PopupSelect;
